package edu.uclm.combTest.core;

import edu.uclm.combTest.core.pairwise.PairsTable;

public class Combination {
	/*
	 * Soit les données
	 * A  B 
	 * 1  2
	 * 2  3
	 * 
	 * 
	 */

	//@ invariant positions.length == elements.length;
	/**
	 * La position est le rang de l'element dans le set A/B/...
	 */
	protected /*@ spec_public @*/long[] positions;
	/**
	 * L'element est donc une information redondante.
	 * Il s'agit donc des valeurs "chaine de car".
	 */
	protected /*@ spec_public @*/Element[] elements;

	protected /*@ spec_public @*/long index;

	//@requires size>=0;
	//@ensures positions.length == size;
	//@ensures elements.length == size;
	//@ensures index==0;
	public Combination(int size) {
		this.positions = new long[size];
		this.elements = new Element[size];
		this.index = 0;
	}

	//@ensures index==\result;
	public /*@pure@*/ long getIndex() {
		return index;
	}


	//@ensures \result ==  this.positions.length;
	public /*@pure@*/ int size() {
		return this.positions.length;
	}

	//@requires position >=0 && position <this.elements.length;
	//@ensures this.elements[ position] == element;

	public void setValue(int position, Element element) {
		this.elements[position] = element;
	}
	
	//@requires position >=0 && position <this.positions.length;
	//@ensures this.positions[ (int) position] == valueIndex;

	public void setValuePosition(long position, long valueIndex) {
		this.positions[(int) position] = valueIndex;
	}


	//@ensures \result == elements;
	public /*@pure@*/final Element[] getElements() {
		return this.elements;
	}
	
	//@requires position >=0 && position <this.positions.length;
	//@ensures \result == positions[position];
	public /*@pure@*/long getPosition(int position) {
		return this.positions[position];
	}

	public /*@pure@*/boolean equals(/*@ nullable @*/Object o) {
		if (!(o instanceof Combination))
			return false;
		Combination c = (Combination) o;
		if (this.size() != c.size())
			return false;
		for (int i = 0; i < this.elements.length; i++)
			if (!(this.elements[i].equals(c.elements[i])))
				return false;
		return true;
	}

	
	public void visitPairs(PairsTable[] pptt) {
		int cont = 0;
		for (int i = 0; i < this.elements.length; i++) {
			for (int j = i + 1; j < this.elements.length; j++) {
				PairsTable pt = pptt[cont++];
				pt.increaseWeightOfPair(this.positions[i], this.positions[j]);
			}
		}
	}

	public /*@pure@*/String toString() {
		String result = "{";
		for (int i = 0; i < elements.length; i++) {
			Element e = elements[i];
			result += e.toString() + ", ";
		}
		if (result.endsWith(", "))
			result = result.substring(0, result.length() - 2);
		result += "}";
		return result;
	}

	public /*@pure@*/String toStringPositions() {
		String result = "{";
		for (int i = 0; i < positions.length; i++) {
			long p = positions[i];
			result += p + ", ";
		}
		result = result.substring(0, result.length() - 2);
		result += "}";
		return result;
	}

	/**
	 * Somme des position de l'element * les diviseurs de l'element.
	 * Correspond au nombre de combinaisons restante sur les paramettres (sets) suivants.
	 * @param divisors
	 */
	public void updateIndex(long[] divisors) {
		this.index = 0;
		for (int i = 0; i < divisors.length; i++) {
			this.index = this.index + this.positions[i] * divisors[i];
		}
	}

}
